package data.dao;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.lightcouch.CouchDbClient;
import org.lightcouch.Response;

import com.fasterxml.jackson.databind.ObjectMapper;

import data.model.ChatMessage;
import data.model.DocumentWithType;
import data.model.Group;
import data.model.Membership;
import data.model.User;

public class DataFacade {

    private CouchDbClient dataSource;

    private final ObjectMapper jsonMapper;

    public DataFacade(String configFile, ObjectMapper jsonMapper) {
        this.jsonMapper = jsonMapper;

        this.dataSource = new CouchDbClient(configFile);
        this.dataSource.design().synchronizeAllWithDb();
    }

    public void shutdown() {
        dataSource.shutdown();
    }

    public boolean isEmailExist(String email) {
        return findUserByEmail(email) != null;
    }

    public void addObject(DocumentWithType object) {
        final Response response = dataSource.save(object);
        object.setId(response.getId());
        object.setRevision(response.getRev());
    }

    public void updateObject(DocumentWithType object) {
        final Response response = dataSource.update(object);
        object.setRevision(response.getRev());
    }

    public void deleteObject(DocumentWithType object) {
        dataSource.remove(object);
    }

    public void deleteObjects(DocumentWithType... objects) {
        final List<Object> data = new ArrayList<Object>();

        for (final DocumentWithType object : objects) {
            data.add(toDeleteMap(object));
        }

        dataSource.bulk(data, true);
    }

    public User findUserByEmail(String email) {
        final List<User> result = dataSource.view("proj1/FindByEmail").key(email).includeDocs(true).query(User.class);
        if (result.size() > 0) {
            return result.get(0);
        } else {
            return null;
        }
    }

    public boolean isRelationshipExist(String groupName, String email, String role) {
        return dataSource.view("proj1/FindMembershipByGroupNameAndUser").key(email, role, groupName).query(Membership.class).size() > 0 ? true : false;
    }

    public Membership findMembership(String groupName, String email, String role) {
        final List<Membership> result = dataSource.view("proj1/FindMembershipByGroupNameAndUser").key(email, role, groupName).includeDocs(true)
                .query(Membership.class);
        return result.size() > 0 ? result.get(0) : null;
    }

    public List<Membership> findMembershipByGroup(String groupId) {
        return dataSource.view("proj1/FindMembershipByGroupId").key(groupId).includeDocs(true).query(Membership.class);
    }

    public List<Membership> findMembershipByUser(String email) {
        return dataSource.view("proj1/FindMembershipByUser").key(email).includeDocs(true).query(Membership.class);
    }

    public List<Group> findGroupByOwner(String email) {
        final List<Membership> membershipList = dataSource.view("proj1/FindMembershipByGroupNameAndUser").startKey(email, "owner")
                .endKey(email, "owner", new Object()).includeDocs(true).query(Membership.class);

        final List<Group> result = new ArrayList<Group>();
        for (final Membership aMembership : membershipList) {
            final Group group = new Group();
            group.setId(aMembership.getGroupId());
            group.setName(aMembership.getGroupName());

            result.add(group);
        }

        return result;
    }

    public Group findGroupById(String groupId) {
        final List<Group> result = dataSource.view("proj1/FindGroupById").key(groupId).includeDocs(true).query(Group.class);
        return result.size() > 0 ? result.get(0) : null;
    }

    private final Map<String, Object> toDeleteMap(DocumentWithType document) {
        final Map<String, Object> result = new HashMap<String, Object>();
        result.put("_id", document.getId());
        result.put("_rev", document.getRevision());
        result.put("_deleted", true);
        return result;
    }

    public long findCurrentMessageCounter(String groupId) {
        try {
            final Map<String, List> response = jsonMapper.readValue(dataSource.view("proj1/FindCurrentMessageCounter").key(groupId).reduce(true)
                    .queryForStream(), Map.class);
            return response.get("rows").size() > 0 ? dataSource.view("proj1/FindCurrentMessageCounter").key(groupId).reduce(true).queryForLong() : 0;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public List<ChatMessage> findMessageAfterCounter(String groupId, long counter) {
        return dataSource.view("proj1/FindMessageAfterCounter").startKey(groupId, counter + 1).endKey(groupId, new Object()).includeDocs(true)
                .query(ChatMessage.class);
    }
}
